/**
  ******************************************************************************
  * @file    X509-util.c
  * @author  dragonchern
  * @version V2.2.0
  * @date    09-15-2014
  * @brief   x509 的一些封装
  * @project linux client
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2013 上海今中网络科技有限公司 </center></h2>
  ******************************************************************************
  */

#include "x509-util.h"



static X509 *load_cert(BIO *cert, int format)
{
    X509 * x=NULL;
    if  (format == DER)
        x = d2i_X509_bio(cert, NULL);
    else if (format == PEM)
        x = PEM_read_bio_X509(cert,NULL,NULL,NULL);//PEM_read_bio_X509_AUX
    else
        goto label_end;

label_end:
    return x;
}


/**
  * @brief   加载der or pem 格式的证书
  * @param   arg1 客户端信息结构体
  * @retval  失败返回NULL，加载证书失败。成功返回 X509 格式的证书
  */
X509 * x509_init_cert(const char * certpath)
{

    BIO     * bio_in    = NULL;
    X509    * userCert  = NULL;

    //根据目录创建相应的bio
    if((bio_in = BIO_new_file(certpath, "r")) == NULL)
    {
        return NULL;
    }


    //根据对应的bio 来加载证书， der 格式或者是 Pem 格式的证书
    if((userCert = load_cert(bio_in, DER)) == NULL)//尝试DER
    {
        if(-1 == BIO_reset(bio_in))//恢复bio
        {
            return NULL;
        }

        MSG(LOG_INFO, "[x509-util.c] [x509_init_cert x509] cert type is pem\n");
        userCert = load_cert(bio_in, PEM);//尝试PEM
    }
    if (bio_in != NULL)
        BIO_free(bio_in);

    return userCert;
}


/**
  * @brief   释放x509 证书的信息
  * @param   x509证书
  * @retval  成功返回0， 失败返回-1， 接收未完成返回1
  */
void x509_free_cert(X509 * userCert)
{
    if(userCert)
        X509_free(userCert);
}

/**
  * @brief   得到证书sn
  * @param   arg1 客户端信息结构体
  * @retval  成功返回0， 失败返回-1， 接收未完成返回1
  */
char * x509_cert_sn(X509 * userCert)
{
	char *ptr = NULL;
	//char buf[64];
    char *sn  = NULL;

	//ZeroMemory(buf, 64);
	if ( (ptr = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(userCert))) == NULL)
		return NULL;
	//snprintf(buf, 64, "%s", ptr);
    sn = strdup( ptr );

	OPENSSL_free(ptr);
	ptr = NULL;

    return sn;
}



/**
  * @brief  获取证书起始日期
  * @param  NULL
  * @retval 证书起始日期
  */
char * x509_cert_start_time(X509 * userCert)
{
	char *ptr = NULL;
	char buf[32];
    char * start_time;

	ZeroMemory(buf, 32);
	ptr = (char *)X509_get_notBefore(userCert)->data;       //unsigned char* data
	strcat(buf, "20");
	strncat(buf, ptr, 2);
	strcat(buf, "-");
	strncat(buf, ptr+2, 2);
	strcat(buf, "-");
	strncat(buf, ptr+4, 2);
	strcat(buf, " ");
	strncat(buf, ptr+6, 2);
	strcat(buf, ":");
	strncat(buf, ptr+8, 2);
	strcat(buf, ":");
	strncat(buf, ptr+10, 2);

	start_time = strdup(buf);
    return start_time;


}

/**
  * @brief  获取证书终止日期
  * @param  NULL
  * @retval 证书终止日期
  */
char * x509_cert_end_time(X509 * userCert)
{
	char *ptr = NULL;
	char buf[32];
	char * end_time;

	ZeroMemory(buf, 32);
	ptr = (char *)X509_get_notAfter(userCert)->data;        //unsigned char * data
	strcat(buf, "20");
	strncat(buf, ptr, 2);
	strcat(buf, "-");
	strncat(buf, ptr+2, 2);
	strcat(buf, "-");
	strncat(buf, ptr+4, 2);
	strcat(buf, " ");
	strncat(buf, ptr+6, 2);
	strcat(buf, ":");
	strncat(buf, ptr+8, 2);
	strcat(buf, ":");
	strncat(buf, ptr+10, 2);

	end_time = strdup(buf);
    return end_time;

}

/**
  * @brief  获取证书使用者的字串，用于bindinfo
  * @param  NULL
  * @retval string for bind, NULL something error happens, pulLen subname length
  */
char * x509_cert_subject_CN(X509 * userCert)
{
    char * temp,*sTemp;
    char issuerBuffer[128] = {0};
    if(!userCert)
        return NULL;

    X509_NAME_oneline(X509_get_subject_name(userCert), issuerBuffer, 128);
    //printf("[DEBUG] [x509_cert_subject_CN] subject name is ####%s\n", (char *)issuerBuffer);

	temp = strtok_r(issuerBuffer, "/", &sTemp);
    while (temp != NULL)
    {
        if (strncmp(temp, "CN=", 3) == 0)
        {
            break;
        }
        temp = strtok_r(NULL, "/", &sTemp);
    }
    return strdup((const char *)(temp+3));
}

/**
  * @brief  获取证书使用者的字串，用于bindinfo
  * @param  NULL
  * @retval NULL or bindinfo string
  *
  *
  * SN: SERIAL NUMBER
  * CN: COMMOM NAME
  * ST: PROVINCE
  * L : CITY
  * O : ORGANIZATION
  * OU: INSTITUTION
  * EA: EMAIL ADDRESS
  * HW: HARDWARE
  * PT: PLATFORM
  * SP: SMART PAHONE
  * +=========+=====+=====+=====+====+====+=====+=====+========+========+=========+=========+==========+
  * | CONTENT | SN: | CN: | ST: | L: | O: | OU: | EA: | HW PT: | HW SN: | OS VER: | SP No.: | SELF VER |
  *	+=========+=====+=====+=====+====+====+=====+=====+========+========+=========+=========+==========+
  * e.g.: 529024408729859892462242183659:James:北京市:shanghai:org:institution:
  *          aa@163.com:PC:BFEBFBFF000206A7:Windows 7:NULL:V1.0000[:ZDMODEL]
  */
char * x509_cert_bindinfo(X509 * userCert, X509_CERT_SUBJECT_T * cert_subject)
{
        char bindinfo[256] = {0};

        char *sn = x509_cert_sn(userCert);
        if(!sn)
        {
            return NULL;
        }
        strcat(bindinfo, sn);
        strcat(bindinfo, ":"); //1

        strcat(bindinfo, cert_subject->common_name);
        strcat(bindinfo, ":");

        strcat(bindinfo, cert_subject->province);
        strcat(bindinfo, ":");

        strcat(bindinfo, cert_subject->city);
        strcat(bindinfo, ":"); //4

        strcat(bindinfo, cert_subject->organization);
        strcat(bindinfo, ":");

        strcat(bindinfo, cert_subject->institution);
        strcat(bindinfo, ":");

        strcat(bindinfo, "null");//cert_subject->email);
        strcat(bindinfo, ":");

        strcat(bindinfo, "SERVER");//cert_subject->province);
        strcat(bindinfo, ":"); //8

        strcat(bindinfo, "null");//cert_subject->province);
        strcat(bindinfo, ":");

        strcat(bindinfo, "linux");//cert_subject->province);
        strcat(bindinfo, ":");

        strcat(bindinfo, "null");//cert_subject->province);
        strcat(bindinfo, ":");

        strcat(bindinfo, "V1.0000");//cert_subject->province);
        //strcat(bindinfo, ":"); //12

        return strdup( bindinfo );
//        strcat(bindinfo, cert_subject->province);
//        strcat(bindinfo, ":");

}

/**
  * @brief  获取证书使用者的元素信息，用于bindinfo
  * @param
  * @retval -1 failed, 0 success
  */
int x509_cert_subject_ele(X509 * userCert, X509_CERT_SUBJECT_T * cert_subject)//int  *pulLen, CERT_SUBJECT_T * cert_subject)
{
	int     index_entry_n               = 0;            /*index of the next matching entry or -1 if not found*/
    //char    subject_name_buf[1024]      = {0};
    char    fun_buf[256]                = {0};

    X509_NAME *subject_x509_name        = NULL;

#ifndef ZeroMemory
#define ZeroMemory(a, b)	memset((a), 0, (b))
#endif

    if ( !userCert )
    {
        return -1;
    }
//    if ( !pulLen )
//    {
//        return NULL;
//    }

    subject_x509_name = X509_get_subject_name(userCert);
    if ( !subject_x509_name )
    {
        return -1;
    }

///-------------------------------------
///typedef struct cert_subject
///{
///    char * serial_num;
///    char * common_name;
///    char * province;
///    char * city;
///    char * organization;
///    char * institution;
///    char * email;
///}CERT_SUBJECT_T;
///-------------------------------------
    ZeroMemory(fun_buf, 256);
    index_entry_n = X509_NAME_get_text_by_NID(subject_x509_name, NID_commonName, fun_buf, 256);
    if(index_entry_n > 0)
    {
        cert_subject->common_name = strdup(fun_buf);
    }
    else
    {
        return -1;//if cannot get the certificate cn name we think the cert is wrong
        //cert_subject->common_name = strdup("null");
    }

    ZeroMemory(fun_buf, 256);
    index_entry_n = X509_NAME_get_text_by_NID(subject_x509_name, NID_stateOrProvinceName, fun_buf, 256);
    if(index_entry_n > 0)
    {
        cert_subject->province = strdup(fun_buf);
    }
    else
    {
        cert_subject->province = strdup("null");
    }

    ZeroMemory(fun_buf, 256);
    index_entry_n = X509_NAME_get_text_by_NID(subject_x509_name, NID_localityName, fun_buf, 256);
    if(index_entry_n > 0)
    {
        cert_subject->city = strdup(fun_buf);
    }
    else
    {
        cert_subject->city = strdup("null");
    }

    ZeroMemory(fun_buf, 256);
    index_entry_n = X509_NAME_get_text_by_NID(subject_x509_name, NID_organizationName, fun_buf, 256);
    if(index_entry_n > 0)
    {
        cert_subject->organization = strdup(fun_buf);
    }
    else
    {
        cert_subject->organization = strdup("null");
    }

    ZeroMemory(fun_buf, 256);
    index_entry_n = X509_NAME_get_text_by_NID(subject_x509_name, NID_organizationalUnitName, fun_buf, 256);
    if(index_entry_n > 0)
    {
        cert_subject->institution = strdup(fun_buf);
    }
    else
    {
        cert_subject->institution = strdup("null");
    }

    // *pulLen = strlen(subject_name_buf);
    //  return strdup((const char *)subject_name_buf);
    return 0;
}


/**
  * @brief  获取证书context
  * @param  NULL
  * @retval 存放证书的队空间
  */
char * x509_cert_context(const char * certpath, int * certSize)
{
#define     CERT_BUF_LEN    2048
    char  * buf;
    int     count = 0;
    int     size = 0;


    FILE * fcert = fopen(certpath,"r");

    if(fcert == NULL)
    {
        printf("[x509 cert context] open cert error\n");
        return  NULL;
    }

    //求得文件的大小
    fseek(fcert, 0, SEEK_END);
    size = ftell(fcert);
    rewind(fcert);
    if(size == 0)
    {
        if(fcert)
            fclose(fcert);
        return NULL;
    }

    *certSize = size;
    buf = ( char* )malloc( sizeof(char) * size );
    count = fread(buf, sizeof(char), CERT_BUF_LEN, fcert);

//    printf("[x509 cert context] file size is ##%d\n", size);
//    printf("[x509 cert context] fread count is ##%d\n", count);
//    printf("[x509 cert context] cert context is#--------------#\n %s
//                 #--------------#\n", (char *)buf);
    if(count != size)
    {
        if(buf != NULL)
        {
            free(buf);
        }
        fclose(fcert);
        return NULL;        //some errors
    }

    if(fcert)
        fclose(fcert);
    return buf;
}





